package cc.siyecao.fastdfs.service;

import cc.siyecao.fastdfs.enums.MetadataFlag;
import cc.siyecao.fastdfs.model.FileInfo;
import cc.siyecao.fastdfs.model.Metadata;
import cc.siyecao.fastdfs.model.StoreFile;

import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Set;

import static cc.siyecao.fastdfs.enums.MetadataFlag.MERGE;
import static cc.siyecao.fastdfs.enums.MetadataFlag.OVERWRITE;

public interface IStorageService {
    /**
     * upload file to storage server (by file name)
     *
     * @param file        local FileName to upload
     * @param fileExtName file ext name, do not include dot(.), null to extract ext name from the local FileName
     * @return 2 elements string array if success:<br>
     * <ul><li>results[0]: the group name to store the file </li></ul>
     * <ul><li>results[1]: the new created FileName</li></ul>
     * return null if fail
     */
    default StoreFile uploadFile(File file, String fileExtName) {
        return uploadFile( file, fileExtName, null );
    }

    /**
     * upload file to storage server (by file name)
     *
     * @param file        local FileName to upload
     * @param fileExtName file ext name, do not include dot(.), null to extract ext name from the local FileName
     * @param metaList    meta info array
     * @return 2 elements string array if success:<br>
     * <ul><li>results[0]: the group name to store the file </li></ul>
     * <ul><li>results[1]: the new created FileName</li></ul>
     * return null if fail
     */
    default StoreFile uploadFile(File file, String fileExtName, Set<Metadata> metaList) {
        return uploadFile( null, file, fileExtName, metaList );
    }

    /**
     * upload file to storage server (by file name)
     *
     * @param groupName   the group name to upload file to, can be empty
     * @param file        local FileName to upload
     * @param fileExtName file ext name, do not include dot(.), null to extract ext name from the local FileName
     * @param metaList    meta info array
     * @return 2 elements string array if success:<br>
     * <ul><li>results[0]: the group name to store the file </li></ul>
     * <ul><li>results[1]: the new created FileName</li></ul>
     * return null if fail
     */
    StoreFile uploadFile(String groupName, File file, String fileExtName, Set<Metadata> metaList);


    /**
     * upload file to storage server (by file name)
     *
     * @param inputStream local FileName to upload
     * @param fileExtName file ext name, do not include dot(.), null to extract ext name from the local FileName
     * @return 2 elements string array if success:<br>
     * <ul><li>results[0]: the group name to store the file </li></ul>
     * <ul><li>results[1]: the new created FileName</li></ul>
     * return null if fail
     */
    default StoreFile uploadFile(InputStream inputStream, long fileSize, String fileExtName) {
        return uploadFile( inputStream, fileSize, fileExtName, null );
    }

    /**
     * upload file to storage server (by file name)
     *
     * @param inputStream local FileName to upload
     * @param fileExtName file ext name, do not include dot(.), null to extract ext name from the local FileName
     * @param metaList    meta info array
     * @return 2 elements string array if success:<br>
     * <ul><li>results[0]: the group name to store the file </li></ul>
     * <ul><li>results[1]: the new created FileName</li></ul>
     * return null if fail
     */
    default StoreFile uploadFile(InputStream inputStream, long fileSize, String fileExtName, Set<Metadata> metaList) {
        return uploadFile( null, inputStream, fileSize, fileExtName, metaList );
    }

    /**
     * upload file to storage server (by file name)
     *
     * @param groupName   the group name to upload file to, can be empty
     * @param inputStream local FileName to upload
     * @param fileExtName file ext name, do not include dot(.), null to extract ext name from the local FileName
     * @param metaList    meta info array
     * @return 2 elements string array if success:<br>
     * <ul><li>results[0]: the group name to store the file </li></ul>
     * <ul><li>results[1]: the new created FileName</li></ul>
     * return null if fail
     */
    StoreFile uploadFile(String groupName, InputStream inputStream, long fileSize, String fileExtName, Set<Metadata> metaList);

    /**
     * upload file to storage server (by file buff)
     *
     * @param fileBuff    file content/buff
     * @param fileExtName file ext name, do not include dot(.)
     * @return 2 elements string array if success:<br>
     * <ul><li>results[0]: the group name to store the file</li></ul>
     * <ul><li>results[1]: the new created FileName</li></ul>
     * return null if fail
     */
    default StoreFile uploadFile(byte[] fileBuff, String fileExtName) {
        return uploadFile( fileBuff, fileExtName, null );
    }

    /**
     * upload file to storage server (by file buff)
     *
     * @param fileBuff    file content/buff
     * @param fileExtName file ext name, do not include dot(.)
     * @param metaList    meta info array
     * @return 2 elements string array if success:<br>
     * <ul><li>results[0]: the group name to store the file</li></ul>
     * <ul><li>results[1]: the new created FileName</li></ul>
     * return null if fail
     */
    default StoreFile uploadFile(byte[] fileBuff, String fileExtName, Set<Metadata> metaList) {
        return uploadFile( null, fileBuff, fileExtName, metaList );
    }

    /**
     * upload file to storage server (by file buff)
     *
     * @param groupName   the group name to upload file to, can be empty
     * @param fileBuff    file content/buff
     * @param fileExtName file ext name, do not include dot(.)
     * @param metaList    meta info array
     * @return 2 elements string array if success:<br>
     * <ul><li>results[0]: the group name to store the file</li></ul>
     * <ul><li>results[1]: the new created FileName</li></ul>
     * return null if fail
     */
    default StoreFile uploadFile(String groupName, byte[] fileBuff, String fileExtName, Set<Metadata> metaList) {
        return uploadFile( groupName, fileBuff, 0, fileBuff.length, fileExtName, metaList );
    }

    /**
     * upload file to storage server (by file buff)
     *
     * @param fileBuff    file content/buff
     * @param offset      start offset of the buff
     * @param fileSize    the fileSize of buff to upload
     * @param fileExtName file ext name, do not include dot(.)
     * @param metaList    meta info array
     * @return 2 elements string array if success:<br>
     * <ul><li>results[0]: the group name to store the file</li></ul>
     * <ul><li>results[1]: the new created FileName</li></ul>
     * return null if fail
     */
    default StoreFile uploadFile(byte[] fileBuff, int offset, int fileSize, String fileExtName, Set<Metadata> metaList) {
        return uploadFile( null, fileBuff, offset, fileSize, fileExtName, metaList );
    }

    /**
     * upload file to storage server (by file buff)
     *
     * @param groupName   the group name to upload file to, can be empty
     * @param fileBuff    file content/buff
     * @param offset      start offset of the buff
     * @param fileSize    the fileSize of buff to upload
     * @param fileExtName file ext name, do not include dot(.)
     * @param metaList    meta info array
     * @return 2 elements string array if success:<br>
     * <ul><li>results[0]: the group name to store the file</li></ul>
     * <ul><li>results[1]: the new created FileName</li></ul>
     * return null if fail
     */
    StoreFile uploadFile(String groupName, byte[] fileBuff, int offset, int fileSize, String fileExtName, Set<Metadata> metaList);

    /**
     * upload file to storage server (by file name, slave file mode)
     *
     * @param groupName      the group name of master file
     * @param masterFileName the master file name to generate the slave file
     * @param prefixName     the prefix name to generate the slave file
     * @param file           local FileName to upload
     * @param fileExtName    file ext name, do not include dot(.), null to extract ext name from the local FileName
     * @return 2 elements string array if success:<br>
     * <ul><li>results[0]: the group name to store the file </li></ul>
     * <ul><li>results[1]: the new created FileName</li></ul>
     * return null if fail
     */
    default StoreFile uploadSlaveFile(String groupName, String masterFileName, String prefixName, File file, String fileExtName) {
        return uploadSlaveFile( groupName, masterFileName, prefixName, file, fileExtName, null );
    }

    /**
     * upload file to storage server (by file name, slave file mode)
     *
     * @param groupName      the group name of master file
     * @param masterFileName the master file name to generate the slave file
     * @param prefixName     the prefix name to generate the slave file
     * @param file           local FileName to upload
     * @param fileExtName    file ext name, do not include dot(.), null to extract ext name from the local FileName
     * @param metaList       meta info array
     * @return 2 elements string array if success:<br>
     * <ul><li>results[0]: the group name to store the file </li></ul>
     * <ul><li>results[1]: the new created FileName</li></ul>
     * return null if fail
     */
    StoreFile uploadSlaveFile(String groupName, String masterFileName, String prefixName, File file, String fileExtName, Set<Metadata> metaList);

    /**
     * upload file to storage server (by file name, slave file mode)
     *
     * @param groupName      the group name of master file
     * @param masterFileName the master file name to generate the slave file
     * @param prefixName     the prefix name to generate the slave file
     * @param inputStream    local FileName to upload
     * @param fileExtName    file ext name, do not include dot(.), null to extract ext name from the local FileName
     * @return 2 elements string array if success:<br>
     * <ul><li>results[0]: the group name to store the file </li></ul>
     * <ul><li>results[1]: the new created FileName</li></ul>
     * return null if fail
     */
    default StoreFile uploadSlaveFile(String groupName, String masterFileName, String prefixName, InputStream inputStream, long fileSize, String fileExtName) {
        return uploadSlaveFile( groupName, masterFileName, prefixName, inputStream, fileSize, fileExtName, null );
    }

    /**
     * upload file to storage server (by file name, slave file mode)
     *
     * @param groupName      the group name of master file
     * @param masterFileName the master file name to generate the slave file
     * @param prefixName     the prefix name to generate the slave file
     * @param inputStream    local FileName to upload
     * @param fileExtName    file ext name, do not include dot(.), null to extract ext name from the local FileName
     * @param metaList       meta info array
     * @return 2 elements string array if success:<br>
     * <ul><li>results[0]: the group name to store the file </li></ul>
     * <ul><li>results[1]: the new created FileName</li></ul>
     * return null if fail
     */
    StoreFile uploadSlaveFile(String groupName, String masterFileName, String prefixName, InputStream inputStream, long fileSize, String fileExtName, Set<Metadata> metaList);

    /**
     * upload file to storage server (by file buff, slave file mode)
     *
     * @param groupName      the group name of master file
     * @param masterFileName the master file name to generate the slave file
     * @param prefixName     the prefix name to generate the slave file
     * @param fileBuff       file content/buff
     * @param fileExtName    file ext name, do not include dot(.)
     * @return 2 elements string array if success:<br>
     * <ul><li>results[0]: the group name to store the file</li></ul>
     * <ul><li>results[1]: the new created FileName</li></ul>
     * return null if fail
     */
    default StoreFile uploadSlaveFile(String groupName, String masterFileName, String prefixName, byte[] fileBuff, String fileExtName) {
        return uploadSlaveFile( groupName, masterFileName, prefixName, fileBuff, fileExtName, null );
    }

    /**
     * upload file to storage server (by file buff, slave file mode)
     *
     * @param groupName      the group name of master file
     * @param masterFileName the master file name to generate the slave file
     * @param prefixName     the prefix name to generate the slave file
     * @param fileBuff       file content/buff
     * @param fileExtName    file ext name, do not include dot(.)
     * @param metaList       meta info array
     * @return 2 elements string array if success:<br>
     * <ul><li>results[0]: the group name to store the file</li></ul>
     * <ul><li>results[1]: the new created FileName</li></ul>
     * return null if fail
     */
    default StoreFile uploadSlaveFile(String groupName, String masterFileName, String prefixName, byte[] fileBuff, String fileExtName, Set<Metadata> metaList) {
        return uploadSlaveFile( groupName, masterFileName, prefixName, fileBuff, 0, fileBuff.length, fileExtName, metaList );
    }

    /**
     * upload file to storage server (by file buff, slave file mode)
     *
     * @param groupName      the group name of master file
     * @param masterFileName the master file name to generate the slave file
     * @param prefixName     the prefix name to generate the slave file
     * @param fileBuff       file content/buff
     * @param offset         start offset of the buff
     * @param fileSize       the fileSize of buff to upload
     * @param fileExtName    file ext name, do not include dot(.)
     * @param metaList       meta info array
     * @return 2 elements string array if success:<br>
     * <ul><li>results[0]: the group name to store the file</li></ul>
     * <ul><li>results[1]: the new created FileName</li></ul>
     * return null if fail
     */
    StoreFile uploadSlaveFile(String groupName, String masterFileName, String prefixName, byte[] fileBuff, int offset, int fileSize, String fileExtName, Set<Metadata> metaList);

    /**
     * upload appender file to storage server (by file name)
     *
     * @param file        local FileName to upload
     * @param fileExtName file ext name, do not include dot(.), null to extract ext name from the local FileName
     * @return 2 elements string array if success:<br>
     * <ul><li>results[0]: the group name to store the file </li></ul>
     * <ul><li>results[1]: the new created FileName</li></ul>
     * return null if fail
     */
    default StoreFile uploadAppenderFile(File file, String fileExtName) {
        return uploadAppenderFile( file, fileExtName, null );
    }

    /**
     * upload appender file to storage server (by file name)
     *
     * @param file        local FileName to upload
     * @param fileExtName file ext name, do not include dot(.), null to extract ext name from the local FileName
     * @param metaList    meta info array
     * @return 2 elements string array if success:<br>
     * <ul><li>results[0]: the group name to store the file </li></ul>
     * <ul><li>results[1]: the new created FileName</li></ul>
     * return null if fail
     */
    default StoreFile uploadAppenderFile(File file, String fileExtName, Set<Metadata> metaList) {
        return uploadAppenderFile( null, file, fileExtName, metaList );
    }

    /**
     * upload appender file to storage server (by file name)
     *
     * @param groupName   the group name to upload file to, can be empty
     * @param file        local FileName to upload
     * @param fileExtName file ext name, do not include dot(.), null to extract ext name from the local FileName
     * @param metaList    meta info array
     * @return 2 elements string array if success:<br>
     * <ul><li>results[0]: the group name to store the file </li></ul>
     * <ul><li>results[1]: the new created FileName</li></ul>
     * return null if fail
     */
    StoreFile uploadAppenderFile(String groupName, File file, String fileExtName, Set<Metadata> metaList);

    /**
     * upload appender file to storage server (by file name)
     *
     * @param inputStream inputStream to upload
     * @param fileExtName file ext name, do not include dot(.), null to extract ext name from the local FileName
     * @return 2 elements string array if success:<br>
     * <ul><li>results[0]: the group name to store the file </li></ul>
     * <ul><li>results[1]: the new created FileName</li></ul>
     * return null if fail
     */
    default StoreFile uploadAppenderFile(InputStream inputStream, long fileSize, String fileExtName) {
        return uploadAppenderFile( inputStream, fileSize, fileExtName, null );
    }

    /**
     * upload appender file to storage server (by file name)
     *
     * @param inputStream inputStream to upload
     * @param fileExtName file ext name, do not include dot(.), null to extract ext name from the local FileName
     * @param metaList    meta info array
     * @return 2 elements string array if success:<br>
     * <ul><li>results[0]: the group name to store the file </li></ul>
     * <ul><li>results[1]: the new created FileName</li></ul>
     * return null if fail
     */
    default StoreFile uploadAppenderFile(InputStream inputStream, long fileSize, String fileExtName, Set<Metadata> metaList) {
        return uploadAppenderFile( null, inputStream, fileSize, fileExtName, metaList );
    }

    /**
     * upload appender file to storage server (by file name)
     *
     * @param groupName   the group name to upload file to, can be empty
     * @param inputStream inputStream to upload
     * @param fileExtName file ext name, do not include dot(.), null to extract ext name from the local FileName
     * @param metaList    meta info array
     * @return 2 elements string array if success:<br>
     * <ul><li>results[0]: the group name to store the file </li></ul>
     * <ul><li>results[1]: the new created FileName</li></ul>
     * return null if fail
     */
    StoreFile uploadAppenderFile(String groupName, InputStream inputStream, long fileSize, String fileExtName, Set<Metadata> metaList);

    /**
     * upload appender file to storage server (by file buff)
     *
     * @param fileBuff    file content/buff
     * @param fileExtName file ext name, do not include dot(.)
     * @return 2 elements string array if success:<br>
     * <ul><li>results[0]: the group name to store the file</li></ul>
     * <ul><li>results[1]: the new created FileName</li></ul>
     * return null if fail
     */
    default StoreFile uploadAppenderFile(byte[] fileBuff, String fileExtName) {
        return uploadAppenderFile( fileBuff, fileExtName, null );
    }

    /**
     * upload appender file to storage server (by file buff)
     *
     * @param fileBuff    file content/buff
     * @param fileExtName file ext name, do not include dot(.)
     * @param metaList    meta info array
     * @return 2 elements string array if success:<br>
     * <ul><li>results[0]: the group name to store the file</li></ul>
     * <ul><li>results[1]: the new created FileName</li></ul>
     * return null if fail
     */
    default StoreFile uploadAppenderFile(byte[] fileBuff, String fileExtName, Set<Metadata> metaList) {
        return uploadAppenderFile( null, fileBuff, fileExtName, metaList );
    }

    /**
     * upload appender file to storage server (by file buff)
     *
     * @param groupName   the group name to upload file to, can be empty
     * @param fileBuff    file content/buff
     * @param fileExtName file ext name, do not include dot(.)
     * @param metaList    meta info array
     * @return 2 elements string array if success:<br>
     * <ul><li>results[0]: the group name to store the file</li></ul>
     * <ul><li>results[1]: the new created FileName</li></ul>
     * return null if fail
     */
    default StoreFile uploadAppenderFile(String groupName, byte[] fileBuff, String fileExtName, Set<Metadata> metaList) {
        return uploadAppenderFile( groupName, fileBuff, 0, fileBuff.length, fileExtName, metaList );
    }

    /**
     * upload appender file to storage server (by file buff)
     *
     * @param fileBuff    file content/buff
     * @param offset      start offset of the buff
     * @param fileSize    the fileSize of buff to upload
     * @param fileExtName file ext name, do not include dot(.)
     * @param metaList    meta info array
     * @return 2 elements string array if success:<br>
     * <ul><li>results[0]: the group name to store the file</li></ul>
     * <ul><li>results[1]: the new created FileName</li></ul>
     * return null if fail
     */
    default StoreFile uploadAppenderFile(byte[] fileBuff, int offset, int fileSize, String fileExtName, Set<Metadata> metaList) {
        return uploadAppenderFile( null, fileBuff, offset, fileSize, fileExtName, metaList );
    }

    /**
     * upload appender file to storage server (by file buff)
     *
     * @param groupName   the group name to upload file to, can be empty
     * @param fileBuff    file content/buff
     * @param offset      start offset of the buff
     * @param fileSize    the fileSize of buff to upload
     * @param fileExtName file ext name, do not include dot(.)
     * @param metaList    meta info array
     * @return 2 elements string array if success:<br>
     * <ul><li>results[0]: the group name to store the file</li></ul>
     * <ul><li>results[1]: the new created FileName</li></ul>
     * return null if fail
     */
    StoreFile uploadAppenderFile(String groupName, byte[] fileBuff, int offset, int fileSize, String fileExtName, Set<Metadata> metaList);

    /**
     * append file to storage server (by file name)
     *
     * @param groupName        the group name of appender file
     * @param appenderFileName the appender FileName
     * @param file             local FileName to append
     * @return 0 for success, != 0 for error (error no)
     */
    int appendFile(String groupName, String appenderFileName, File file);

    /**
     * append file to storage server (by file name)
     *
     * @param groupName        the group name of appender file
     * @param appenderFileName the appender FileName
     * @param inputStream      local FileName to append
     * @return 0 for success, != 0 for error (error no)
     */
    int appendFile(String groupName, String appenderFileName, InputStream inputStream, long fileSize);

    /**
     * append file to storage server (by file buff)
     *
     * @param groupName        the group name of appender file
     * @param appenderFileName the appender FileName
     * @param fileBuff         file content/buff
     * @return 0 for success, != 0 for error (error no)
     */
    default int appendFile(String groupName, String appenderFileName, byte[] fileBuff) {
        return appendFile( groupName, appenderFileName, fileBuff, 0, fileBuff.length );
    }

    /**
     * append file to storage server (by file buff)
     *
     * @param groupName        the group name of appender file
     * @param appenderFileName the appender FileName
     * @param fileBuff         file content/buff
     * @param offset           start offset of the buff
     * @param fileSize         the fileSize of buff to append
     * @return 0 for success, != 0 for error (error no)
     */
    int appendFile(String groupName, String appenderFileName, byte[] fileBuff, int offset, int fileSize);

    /**
     * modify appender file to storage server (by file name)
     *
     * @param groupName        the group name of appender file
     * @param appenderFileName the appender FileName
     * @param fileOffset       the offset of appender file
     * @param file             local FileName to append
     * @return 0 for success, != 0 for error (error no)
     */
    int modifyFile(String groupName, String appenderFileName, File file, int fileOffset);

    /**
     * modify appender file to storage server (by file name)
     *
     * @param groupName        the group name of appender file
     * @param appenderFileName the appender FileName
     * @param fileOffset       the offset of appender file
     * @param inputStream      local FileName to append
     * @return 0 for success, != 0 for error (error no)
     */
    int modifyFile(String groupName, String appenderFileName, InputStream inputStream, long fileSize, int fileOffset);

    /**
     * modify appender file to storage server (by file buff)
     *
     * @param groupName        the group name of appender file
     * @param appenderFileName the appender FileName
     * @param fileOffset       the offset of appender file
     * @param fileBuff         file content/buff
     * @return 0 for success, != 0 for error (error no)
     */
    default int modifyFile(String groupName, String appenderFileName, byte[] fileBuff, int fileOffset) {
        return modifyFile( groupName, appenderFileName, fileBuff, fileOffset, 0, fileBuff.length );
    }

    /**
     * modify appender file to storage server (by file buff)
     *
     * @param groupName        the group name of appender file
     * @param appenderFileName the appender FileName
     * @param fileOffset       the offset of appender file
     * @param fileBuff         file content/buff
     * @param bufferOffset     start offset of the buff
     * @param bufferfileSize   the fileSize of buff to modify
     * @return 0 for success, != 0 for error (error no)
     */
    int modifyFile(String groupName, String appenderFileName, byte[] fileBuff, int fileOffset, int bufferOffset, int bufferfileSize);

    /**
     * regenerate FileName for appender file
     * V6.02 开始支持的regenerate_appender_filename 方法将文件改名为普通文件
     *
     * @param groupName        the group name of appender file
     * @param appenderFileName the appender FileName
     * @return 2 elements string array if success:<br>
     * <ul><li> results[0]: the group name to store the file</li></ul>
     * <ul><li> results[1]: the new created FileName</li></ul>
     * return null if fail
     */
    StoreFile regenerateAppenderFileName(String groupName, String appenderFileName);

    /**
     * delete file from storage server
     *
     * @param groupName      the group name of storage server
     * @param remoteFileName FileName on storage server
     * @return 0 for success, none zero for fail (error code)
     */
    int deleteFile(String groupName, String remoteFileName);

    /**
     * truncate appender file to size 0 from storage server
     *
     * @param groupName        the group name of storage server
     * @param appenderFileName the appender FileName
     * @return 0 for success, none zero for fail (error code)
     */
    default int truncateFile(String groupName, String appenderFileName) {
        return truncateFile( groupName, appenderFileName, 0 );
    }

    /**
     * truncate appender file from storage server
     *
     * @param groupName         the group name of storage server
     * @param appenderFileName  the appender FileName
     * @param truncatedFileSize truncated file size
     * @return 0 for success, none zero for fail (error code)
     */
    int truncateFile(String groupName, String appenderFileName, long truncatedFileSize);

    /**
     * download file from storage server
     *
     * @param groupName      the group name of storage server
     * @param remoteFileName FileName on storage server
     * @return file content/buff, return null if fail
     */
    default byte[] downloadFile(String groupName, String remoteFileName) {
        return downloadFile( groupName, remoteFileName, 0, 0 );
    }

    /**
     * download file from storage server
     *
     * @param groupName      the group name of storage server
     * @param remoteFileName FileName on storage server
     * @param fileOffset     the start offset of the file
     * @param downloadBytes  download bytes, 0 for remain bytes from offset
     * @return file content/buff, return null if fail
     */
    byte[] downloadFile(String groupName, String remoteFileName, long fileOffset, long downloadBytes);

    /**
     * download file from storage server
     *
     * @param groupName      the group name of storage server
     * @param remoteFileName FileName on storage server
     * @return file content/buff, return null if fail
     */
    default int downloadFile(String groupName, String remoteFileName, OutputStream out) {
        return downloadFile( groupName, remoteFileName, out, 0, 0 );
    }

    /**
     * download file from storage server
     *
     * @param groupName      the group name of storage server
     * @param remoteFileName FileName on storage server
     * @param fileOffset     the start offset of the file
     * @param downloadBytes  download bytes, 0 for remain bytes from offset
     * @return file content/buff, return null if fail
     */
    int downloadFile(String groupName, String remoteFileName, OutputStream out, long fileOffset, long downloadBytes);

    /**
     * download file from storage server
     *
     * @param groupName      the group name of storage server
     * @param remoteFileName FileName on storage server
     * @param file           FileName on local
     * @return 0 success, return none zero errno if fail
     */
    default int downloadFile(String groupName, String remoteFileName, File file) {
        return downloadFile( groupName, remoteFileName, file, 0, 0 );
    }

    /**
     * download file from storage server
     *
     * @param groupName      the group name of storage server
     * @param remoteFileName FileName on storage server
     * @param fileOffset     the start offset of the file
     * @param downloadBytes  download bytes, 0 for remain bytes from offset
     * @param file           FileName on local
     * @return 0 success, return none zero errno if fail
     */
    int downloadFile(String groupName, String remoteFileName, File file, long fileOffset, long downloadBytes);

    /**
     * get all metadata items from storage server
     *
     * @param groupName      the group name of storage server
     * @param remoteFileName FileName on storage server
     * @return meta info array, return null if fail
     */
    Set<Metadata> getMetadata(String groupName, String remoteFileName);


    /**
     * set metadata items to storage server
     *
     * @param groupName      the group name of storage server
     * @param remoteFileName FileName on storage server
     * @param metaList       meta item array
     *                       <ul><li> ProtoCommon.STORAGE_SET_METADATA_FLAG_OVERWRITE: overwrite all old
     *                       metadata items</li></ul>
     *                       <ul><li> ProtoCommon.STORAGE_SET_METADATA_FLAG_MERGE: merge, insert when
     *                       the metadata item not exist, otherwise update it</li></ul>
     * @return 0 for success, !=0 fail (error code)
     */
    default int setMetadata(String groupName, String remoteFileName, Set<Metadata> metaList) {
        return setMetadata( groupName, remoteFileName, metaList, OVERWRITE );
    }

    /**
     * set metadata items to storage server
     *
     * @param groupName      the group name of storage server
     * @param remoteFileName FileName on storage server
     * @param metaList       meta item array
     *                       <ul><li> ProtoCommon.STORAGE_SET_METADATA_FLAG_OVERWRITE: overwrite all old
     *                       metadata items</li></ul>
     *                       <ul><li> ProtoCommon.STORAGE_SET_METADATA_FLAG_MERGE: merge, insert when
     *                       the metadata item not exist, otherwise update it</li></ul>
     * @return 0 for success, !=0 fail (error code)
     */
    default int mergeMetadata(String groupName, String remoteFileName, Set<Metadata> metaList) {
        return setMetadata( groupName, remoteFileName, metaList, MERGE );
    }

    /**
     * set metadata items to storage server
     *
     * @param groupName      the group name of storage server
     * @param remoteFileName FileName on storage server
     * @param metaList       meta item array
     * @param metadataFlag   flag, can be one of following values: <br>
     *                       <ul><li> ProtoCommon.STORAGE_SET_METADATA_FLAG_OVERWRITE: overwrite all old
     *                       metadata items</li></ul>
     *                       <ul><li> ProtoCommon.STORAGE_SET_METADATA_FLAG_MERGE: merge, insert when
     *                       the metadata item not exist, otherwise update it</li></ul>
     * @return 0 for success, !=0 fail (error code)
     */
    int setMetadata(String groupName, String remoteFileName, Set<Metadata> metaList, MetadataFlag metadataFlag);

    /**
     * get file info decoded from the FileName, fetch from the storage if necessary
     *
     * @param groupName      the group name
     * @param remoteFileName the FileName
     * @return FileInfo object for success, return null for fail
     */
    FileInfo getFileInfo(String groupName, String remoteFileName);

    /**
     * get file info from storage server
     *
     * @param groupName      the group name of storage server
     * @param remoteFileName FileName on storage server
     * @return FileInfo object for success, return null for fail
     */
    FileInfo queryFileInfo(String groupName, String remoteFileName);
}
